/*
 *  sparticlusSimulation.cpp
 *  sparticlus
 *
 *  Created by Lita Gratrix on 4/12/10.
 *  Copyright 2010 Lita Gratrix. All rights reserved.
 *
 */

#include "sparticlusSimulation.h"

//constructor
sparticlusSimulation::sparticlusSimulation() {
}

//destructor
sparticlusSimulation::~sparticlusSimulation(){	
}

//helper function to write out errors
MStatus sparticlusSimulation::throwError(MString errorString, MStatus status){
	MGlobal::displayError(errorString);
	exit(-1);
}

//helper function to set the frames per second value based on the current time unit
int sparticlusSimulation::setFPS(){
	int fps;
	MTime::Unit timeUnit;
	timeUnit = MTime::uiUnit();
	switch (MTime::uiUnit()) {
		case MTime::kGames:
			fps = 15;
			break;
		case MTime::kFilm:
			fps = 24;
			break;
		case MTime::kPALFrame:
			fps = 25;
			break;
		case MTime::kNTSCFrame:
			fps = 30;
			break;
		case MTime::kShowScan:
			fps = 48;
			break;
		case MTime::kPALField:
			fps = 50;
			break;
		case MTime::kNTSCField:
			fps = 60;
			break;
		case MTime::k2FPS:
			fps = 2;
			break;
		case MTime::k3FPS:
			fps = 3;
			break;
		case MTime::k4FPS:
			fps = 4;
			break;
		case MTime::k5FPS:
			fps = 5;
			break;
		case MTime::k6FPS:
			fps = 6;
			break;
		case MTime::k8FPS:
			fps = 8;
			break;
		case MTime::k10FPS:
			fps = 10;
			break;
		case MTime::k12FPS:
			fps = 12;
			break;
		case MTime::k16FPS:
			fps = 16;
			break;
		case MTime::k20FPS:
			fps = 20;
			break;
		case MTime::k40FPS:
			fps = 40;
			break;
		case MTime::k75FPS:
			fps = 75;
			break;
		case MTime::k80FPS:
			fps = 80;
			break;
		case MTime::k100FPS:
			fps = 100;
			break;
		case MTime::k120FPS:
			fps = 120;
			break;
		case MTime::k125FPS:
			fps = 125;
			break;			
		default:
			fps = -1;
			break;
	}
	return fps;
}

MStatus sparticlusSimulation::simulate( MObjectArray particleSystems, int startFrame, int finishFrame, int sampleRate ){
	
	MGlobal::displayInfo("entering simulate");
	MString info;
	MStatus status;
	MVectorArray posArray, velArray;
	int fps;
	int partCount;
	
	//determine frames per second, used in calculating new position
	fps = sparticlusSimulation::setFPS();
	
	//determine increment per frame for calculation loop
	int start = startFrame;
	if(sampleRate < 1){
		sampleRate = 1;
	}
	float incr = 1.0/sampleRate;
	
	//for each sample in the scene, process simulation data
	int i;
	int numberOfPartSystems = particleSystems.length();
	
	//////////////////////
	//pre-simulation processing of the particle systems
	//////////////////////
	
	//set the particles nodes to not dynamic, as we will be handling the dynamics ourselves
	for(i = 0; i < numberOfPartSystems; i++){
		MFnParticleSystem particle(particleSystems[i]);
		MString isDynamic = "isDynamic";
		MPlug particleDynamicPlug = particle.findPlug(isDynamic, true, &status);
		if(status != MS::kSuccess){
			sparticlusSimulation::throwError("error getting isDynamic plug", status);
		}
		particleDynamicPlug.setValue(0);
	}
	
	/////////////////////
	//Simulation
	/////////////////////
	for(start; start < finishFrame; start += incr){
		//printf("For Frame: %d\n", start);
		
		//for each particle system in the scene, get position and velocity 
		//data and calculate new position
		for(i = 0; i < numberOfPartSystems; i++){
			//set the particle system object to a function set in order to get data from it
			//and get the position and velocity arrays
			MFnParticleSystem particle(particleSystems[i]);
			partCount = particle.count();
			particle.position(posArray);
			particle.velocity(velArray);
			
			//convert the vector array into 2D arrays of floats (cuda compute capability GPUs
			//less than 1.3 cannot handle doubles
			float *posArrayFloat = new float[partCount * kVArraySize];
			float *velArrayFloat = new float[partCount * kVArraySize];
			
			posArray.get((myVector) posArrayFloat);
			velArray.get((myVector) velArrayFloat);
			
			//dimension an array to store the results of the calculation
			float *posPrimeArray_h = new float[partCount * kVArraySize];
			 
			//actually perform the calculation, but only if there are particles to run it on
			if(particle.count() > 0){
				simKernel(fps, incr, posArrayFloat, velArrayFloat, partCount, posPrimeArray_h);
			}
				
			//convert array to MVectorArray
			MVectorArray posPrime((myVector)posPrimeArray_h, partCount);
			
			//udpate particle attributes
			particle.setPerParticleAttribute(MString("position"), posPrime);
			
			//free memory
			delete [] (myVector)posArrayFloat;
			delete [] (myVector)velArrayFloat;
			delete [] (myVector)posPrimeArray_h;
			
			posArray.clear();
			velArray.clear();
		}
		
		//set the particle time ahead and move global time ahead
		MGlobal::viewFrame(start);
	}
	
	////////////////////////
	//Post simulation processing
	////////////////////////
	
	//restore the node to being dynamic
	for(i = 0; i < numberOfPartSystems; i++){
		MFnParticleSystem particle(particleSystems[i]);
		MString isDynamic = "isDynamic";
		MPlug particleDynamicPlug = particle.findPlug(isDynamic, true, &status);
		if(status != MS::kSuccess){
			sparticlusSimulation::throwError("error getting isDynamic plug", status);
		}
		particleDynamicPlug.setValue(1);
		
	}
	
	cout<< "Done with Simulation" << endl;
	status = MS::kSuccess;
	return status;
}


